﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Nuclex.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace DataModels
{
    public class ComputerInput
    {
        public const int KEY_DELAY_IN_MILLISECOUNDS = 120;

        private struct keyDelayMap
        {
            public int timeLeftInMillisecounds;
            public Keys key;
            public keyDelayMap reduceTime(int milliSecounds)
            {
                keyDelayMap returnMe = new keyDelayMap();
                returnMe.timeLeftInMillisecounds = timeLeftInMillisecounds - milliSecounds;
                returnMe.key = key;
                return returnMe;
            }

        }

        private InputManager nuclexInput;
        String enteredText = "";
        private static ComputerInput me;
        private LinkedList<Keys> pressedKeys = new LinkedList<Keys>();
        private LinkedList<keyDelayMap> keyDealy = new LinkedList<keyDelayMap>();

        private bool leftMouseUpToDown = false;
        private bool leftMouseDownToUp = false;
        private bool rightMouseUpToDown = false;
        private bool rightMouseDownToUp = false;
        private bool isMouseMoved = false;

        private ComputerInput(Microsoft.Xna.Framework.GameServiceContainer services,
            IntPtr windowHandle,
            GameComponentCollection components)
        {
            nuclexInput = new InputManager(services, windowHandle);
            subscribeToKeyboard();
            subscribeToMouse();
            components.Add(nuclexInput);
        }

        public static ComputerInput getComputerInput()
        {
            if (me != null)
                return me;
            else throw new Exception("Computer input silgleton was not initialized," +
            "please use createComputerInput(...) first");

        }

        public static void createComputerInput(Microsoft.Xna.Framework.GameServiceContainer services,
            IntPtr windowHandle,
            GameComponentCollection components)
        {
            me = new ComputerInput(services, windowHandle, components);
        }

        private void subscribeToKeyboard()
        {
            nuclexInput.GetKeyboard().CharacterEntered += keyboardCharacterEntered;
            nuclexInput.GetKeyboard().KeyPressed += keyPressed;
            nuclexInput.GetKeyboard().KeyReleased += keyRealeased;
        }

        private void subscribeToMouse()
        {
            nuclexInput.GetMouse().MouseButtonPressed += mousePressed;
            nuclexInput.GetMouse().MouseButtonReleased += mouseRealeased;
            nuclexInput.GetMouse().MouseMoved += mouseMoved;
        }


        private void mouseMoved(float x, float y)
        {
            isMouseMoved = true;
        }

        public bool getIsMouseMovedAndReset()
        {
            bool returnMe = isMouseMoved;
            isMouseMoved = false;
            return returnMe;
        }

        private void mousePressed(MouseButtons buttons)
        {
            if (buttons.Equals(MouseButtons.Left))
            {
                leftMouseUpToDown = true;
            }
            if (buttons.Equals(MouseButtons.Right))
            {
                rightMouseUpToDown = true;
            }

        }
        private void mouseRealeased(MouseButtons buttons)
        {
            if (buttons.Equals(MouseButtons.Left))
            {
                leftMouseDownToUp = true;
            }
            if (buttons.Equals(MouseButtons.Right))
            {
                rightMouseDownToUp = true;
            }
        }

        private void keyRealeased(Keys key)
        {
            removeAll(key);
        }

        private void keyboardCharacterEntered(char character)
        {
            enteredText += character;
        }

        private bool isOnDelay(Keys key)
        {
            LinkedListNode<keyDelayMap> curNode = keyDealy.First;
            while (curNode != null)
            {
                if (curNode.Value.key.Equals(key))
                    return true;
                curNode = curNode.Next;
            }
            return false;
        }

        private void keyPressed(Keys pressedKey)
        {
            pressedKeys.AddLast(pressedKey);
        }

        public void addDelay(Keys key)
        {
            keyDelayMap newDelay;
            newDelay.key = key;
            newDelay.timeLeftInMillisecounds = KEY_DELAY_IN_MILLISECOUNDS;
            keyDealy.AddLast(newDelay);
        }

        public String getAndResetInput()
        {
            String returnInput = getKeyboardInput();
            deleateCurrentInput();
            return returnInput;

        }

        public String getKeyboardInput()
        {
            return enteredText;
        }

        private void deleateCurrentInput()
        {
            enteredText = "";
        }

        public bool isKeyPressedDown(String keyString)
        {
            if (ContainsIgnoreCase(enteredText, keyString))
                return true;
            else return false;
        }

        private bool ContainsIgnoreCase(String baseString, String compareString)
        {
            if (baseString.Contains(compareString.ToUpper()) ||
                baseString.Contains(compareString.ToLower()))
                return true;
            else return false;
        }

        public bool isKeyPressedDown(Keys key)
        {
            LinkedListNode<Keys> curNode = pressedKeys.First;
            while (curNode != null)
            {
                if (curNode.Value.Equals(key))
                    return true;

                curNode = curNode.Next;
            }
            return false;

        }

        public bool getIsKeyPressedDownWithDelay(Keys key)
        {
            bool returnBool = false;
            if (!isOnDelay(key))
            {
                returnBool = isKeyPressedDown(key);
                if (returnBool)
                    addDelay(key);

            }
            return returnBool;
        }

        private void removeAll(Keys key)
        {
            while (pressedKeys.Contains(key))
            {
                pressedKeys.Remove(key);
            }
        }

        public Point getMouseLocation()
        {
            Point returnLocation;

            returnLocation.X = nuclexInput.GetMouse().GetState().X;
            returnLocation.Y = nuclexInput.GetMouse().GetState().Y;

            return returnLocation;
        }

        public bool isMouseLeftButtonDown()
        {
            if (nuclexInput.GetMouse().GetState().LeftButton == ButtonState.Pressed)
                return true;
            return false;
        }

        public bool isMouseLeftButtonUp()
        {
            if (nuclexInput.GetMouse().GetState().LeftButton == ButtonState.Released)
                return true;
            return false;
        }

        public bool isMouseLeftButtonDownClick()
        {

            return leftMouseUpToDown;
        }

        public bool isMouseLeftButtonUpClick()
        {

            return leftMouseDownToUp;
        }

        public bool isMouseRightButtonDown()
        {
            if (nuclexInput.GetMouse().GetState().RightButton == ButtonState.Pressed)
                return true;
            return false;
        }

        public bool isMouseRightButtonUp()
        {
            if (nuclexInput.GetMouse().GetState().RightButton == ButtonState.Released)
                return true;
            return false;
        }

        public bool isMouseRightButtonDownClick()
        {

            return rightMouseUpToDown;
        }

        public bool isMouseRightButtonUpClick()
        {

            return rightMouseDownToUp;
        }

        private void mouseClickActionTimeout()
        {
            leftMouseUpToDown = false;
            leftMouseDownToUp = false;
            rightMouseUpToDown = false;
            rightMouseDownToUp = false;
            //reduceKeyDelay(100);
        }

        private void reduceKeyDelay(int millisecounds)
        {
            LinkedListNode<keyDelayMap> curNode = this.keyDealy.First;
            LinkedListNode<keyDelayMap> removeMe;
            while (curNode != null)
            {
                curNode.Value = curNode.Value.reduceTime(millisecounds);


                if (curNode.Value.timeLeftInMillisecounds <= 0)
                {
                    removeMe = curNode;
                    curNode = curNode.Next;
                    keyDealy.Remove(removeMe);
                }
                else
                    curNode = curNode.Next;
            }
        }

        public void updateTime(GameTime gameTime)
        {
            int milliSecs = (int)gameTime.ElapsedGameTime.TotalMilliseconds;
            mouseClickActionTimeout();
            reduceKeyDelay(milliSecs);

        }

    }
}
